package com.xinziruo.richclient.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;

import com.xinziruo.richclient.util.ExceptionUtils;
import com.xinziruo.richclient.util.FileUtils;
import com.xinziruo.richclient.util.IOUtils;
import com.xinziruo.richclient.util.PathUtils;
import com.xinziruo.richclient.util.filecommand.FileCommandInvoker;
import com.xinziruo.richclient.util.filecommand.impl.DeleteFileCommand;
import com.xinziruo.richclient.util.filecommand.impl.MakeFileCommand;
import com.xinziruo.richclient.util.filecommand.impl.WriteBytesToFileCommand;
import com.xinziruo.richclient.util.filecommand.impl.WriteFileToCommand;

/**
 * 
 * @author loudyn
 *
 */
public class OCRUtils {
	
	/**
	 * 
	 * @param url
	 * @return
	 */
	public static OCR fromUrl(String url){
		return new OCR().url(url);
	}
	
	/**
	 * 
	 * @param in
	 * @return
	 */
	public static OCR fromStream(InputStream in){
		return new OCR().stream(in);
	}
	
	/**
	 * 
	 * @param file
	 * @return
	 */
	public static OCR fromFile(File file){
		return new OCR().file(file);
	}
	
	/**
	 * 
	 * @param path
	 * @return
	 */
	public static OCR fromPath(String path){
		return new OCR().path(path);
	}

	public static final class OCR {
		private byte[] imgBytes;

		protected OCR() {
		}

		protected OCR url(String url) {
			try {

				return stream(new URL(url).openStream());
			} catch (Exception e) {
				throw ExceptionUtils.toUnchecked(e);
			}
		}

		protected OCR path(String path) {
			return file(new File(path));
		}

		protected OCR file(File file) {
			try {
				return stream(new FileInputStream(file));
			} catch (Exception e) {
				throw ExceptionUtils.toUnchecked(e);
			}
		}

		protected OCR stream(InputStream in) {

			ByteArrayOutputStream out = null;
			try {

				out = new ByteArrayOutputStream();
				IOUtils.piping(in, out);
				this.imgBytes = out.toByteArray();
				return this;
			} catch (Exception e) {
				throw ExceptionUtils.toUnchecked(e);
			} finally {
				IOUtils.freeQuietly(in, out);
			}
		}

		public String ocr() {

			OCRConf conf = OCRConf.me();
			String tempImg = createImage(conf);
			String output = createOutput(conf);

			try {

				ocr(conf, tempImg, output);
				return extractOutput(output);
			} finally {

				new FileCommandInvoker().command(new DeleteFileCommand(tempImg))
										.command(new DeleteFileCommand(output.concat(".txt")))
										.invoke();
				imgBytes = null;
			}
		}

		private String extractOutput(String output) {

			ByteArrayOutputStream baos = null;
			try {
				
				baos = new ByteArrayOutputStream();
				new WriteFileToCommand(new File(output.concat(".txt")), baos).execute();
				return baos.toString("UTF-8");
			} catch (Exception e) {
				throw ExceptionUtils.toUnchecked(e);
			} finally {
				IOUtils.freeQuietly(baos);
			}
		}

		private void ocr(OCRConf conf, String tempImg, String output) {
			try {

				ProcessBuilder pb = new ProcessBuilder(conf.getOCRExe(), tempImg, output, "-l", "eng", "-psm", "7");
				int status = pb.start().waitFor();
				if (status != 0) {
					throw new IllegalStateException("Can't ocr the image");
				}

			} catch (Exception e) {
				throw ExceptionUtils.toUnchecked(e);
			}
		}

		private String createOutput(OCRConf conf) {

			String output = PathUtils.asPlatform(FileUtils.joinPaths(
																		conf.getOCRContextTempDir(),
																		"output",
																		UUID.randomUUID().toString()
																	)
												);

			new MakeFileCommand(output).execute();
			return output;
		}

		private String createImage(OCRConf conf) {
			try {

				String tempImg = PathUtils.asPlatform(FileUtils.joinPaths(
																			conf.getOCRContextTempDir(),
																			"img",
																			UUID.randomUUID().toString()
																		)
													);

				new FileCommandInvoker().command(new MakeFileCommand(tempImg))
										.command(new WriteBytesToFileCommand(tempImg, imgBytes))
										.invoke();
				return tempImg;
			} catch (Exception e) {
				throw ExceptionUtils.toUnchecked(e);
			}
		}

	}

	private static final class OCRConf {

		private final String context;

		public OCRConf(String ctx) {
			this.context = ctx;
		}

		public String getOCRExe() {
			return PathUtils.asPlatform(EXE);
		}

		public String getOCRContextTempDir() {
			return PathUtils.asPlatform("E:/tesseract/temp");
		}

		private static final String EXE = "C:/Program Files/Tesseract-OCR/tesseract.exe";
		private static OCRConf me;
		static {

			String path = ClassLoader.getSystemClassLoader().getResource(".").getFile();
			
			// on window the path start with '/', what happen ? 
			if(isWindowPlatform()){
				path = path.replaceAll("^[/\\\\]+([^/\\\\]+)", "$1");
			}
			
			try{
				
				path  = new File(path).getCanonicalPath();
			}catch (Exception e) {
			}
			
			me = new OCRConf(path);
		}

		public static OCRConf me() {
			return me;
		}

		private static boolean isWindowPlatform() {
			return !StringUtils.equalsIgnoreCase("/", File.separator);
		}
	}
}
